package itpenguin.train.london;

import itpenguin.train.common.Position;
import itpenguin.train.common.Train;
import itpenguin.train.common.TrainFeed;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import com.google.transit.realtime.GtfsRealtime.VehiclePosition.VehicleStopStatus;

public class Tlf implements TrainFeed {

    @Override
    public List<Train> getCurrentPositions() throws IOException {
        
        Map<String, Tube> tubeDic = new HashMap<String, Tube>();
        String[] lines = new String[] {"B", "C", "D", "H", "J", "M", "N", "P", "V", "W"};
        
        for (String lineId : lines) {
            try {
				fetchFeed(tubeDic, lineId);
			} catch (FactoryConfigurationError | XMLStreamException e) {
				throw new IOException(e);
			}
        }
        
        List<Train> positions = new ArrayList<Train>();
        for(Map.Entry<String, Tube> entry : tubeDic.entrySet()) {
            Tube tube = entry.getValue();
            Position pos = Dictionary.getStationPosition(tube.stationCode);
            if (pos == null) {
                System.out.println(tube.stationCode + " " + tube.stationName + " " + tube.routeId);
                continue;
            }
            Train train = new Train(pos.getLatitude(), pos.getLongitude(), 
                    tube.timeToStation < 0 ? VehicleStopStatus.STOPPED_AT : 
                        tube.timeToStation == 0 ? VehicleStopStatus.INCOMING_AT : VehicleStopStatus.IN_TRANSIT_TO, 
                                entry.getKey(), tube.tripId, tube.routeId, tube.lastUpdateTime);
            train.setBoundFor(tube.boundFor);
            
            positions.add(train);
        }
        return positions;
    }

    private void fetchFeed(Map<String, Tube> tubeDic, String lineId) throws FactoryConfigurationError, IOException, XMLStreamException {
        InputStream is = null;
        XMLStreamReader reader = null;
        XMLInputFactory factory = XMLInputFactory.newInstance();
        
        try {
            is = new URL("http://cloud.tfl.gov.uk/TrackerNet/PredictionSummary/" + lineId).openStream();
            reader = factory.createXMLStreamReader(is);
            String stationCode = null;
            String stationName = null;
            String platFormCode = null;
            String platFormName = null;
            Date timeStamp = null;
            
            while (reader.hasNext()) {
                int eventType = reader.next();
                
                if (eventType == XMLStreamReader.START_ELEMENT) {
                	if (reader.getName().getLocalPart().equals("Time")) {
                		SimpleDateFormat dtf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                		dtf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
                		try {
							timeStamp = dtf.parse(reader.getAttributeValue(0));
						} catch (ParseException e) {
							throw new IOException(e);
						}
                	} else if (reader.getName().getLocalPart().equals("S")) {
                        stationCode = reader.getAttributeValue(0);
                        stationName = reader.getAttributeValue(1);
                    } else if (reader.getName().getLocalPart().equals("P")) {
                        platFormCode = reader.getAttributeValue(1);
                        platFormName = reader.getAttributeValue(0);
                    } else if (reader.getName().getLocalPart().equals("T")) {
                        String trainId = reader.getAttributeValue(0);
                        String ttsStr = reader.getAttributeValue(3);
                        int timeToStation = 0;
                        if ("due".equals(ttsStr)) {
                            timeToStation = -1;
                        } else if (!"-".equals(ttsStr) && !"due".equals(ttsStr)) {
                            String[] ttsStrTm = reader.getAttributeValue(3).split(":");
                            timeToStation = Integer.parseInt(ttsStrTm[0]) * 100 + Integer.parseInt(ttsStrTm[1]);
                        }
                        Tube tube;
                        if (tubeDic.containsKey(trainId)) {
                            tube = tubeDic.get(trainId);
                            if (tube.timeToStation >= timeToStation) {
                                tube.stationCode = stationCode;
                                tube.stationName = stationName;
                                tube.platFormCode = platFormCode;
                                tube.platFormName = platFormName;
                                tube.timeToStation = timeToStation;
                            }
                        } else {
                            tube = new Tube();
                            tube.tripId = reader.getAttributeValue(1);
                            tube.destCode = reader.getAttributeValue(2);
                            tube.timeToStation = timeToStation;
                            tube.status = reader.getAttributeValue(4);
                            tube.boundFor = reader.getAttributeValue(5);
                            tube.stationCode = stationCode;
                            tube.stationName = stationName;
                            tube.platFormCode = platFormCode;
                            tube.platFormName = platFormName;
                            tube.timeToStation = timeToStation;
                            tube.routeId = lineId;
                            tube.lastUpdateTime = timeStamp;
                            tubeDic.put(trainId, tube);
                        }
                    }
                } else if (eventType == XMLStreamReader.END_ELEMENT) {
                    if (reader.getName().getLocalPart().equals("S")) {
                        stationCode = null;
                        stationName = null;
                    } else if (reader.getName().getLocalPart().equals("P")) {
                        platFormCode = null;
                        platFormName = null;
                    }
                }
            }
            
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (XMLStreamException ex) {}
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {}
            }
        }
    }

    private static class Tube {
        public String stationCode;
        public String stationName;
        @SuppressWarnings("unused")
        public String platFormCode;
        @SuppressWarnings("unused")
        public String platFormName;
        public String tripId;
        @SuppressWarnings("unused")
        public String destCode;
        public int timeToStation;
        @SuppressWarnings("unused")
        public String status;
        public String boundFor;
        public String routeId;
        public Date lastUpdateTime;
    }
    
}
